package mirrormonkey.util.annotations.parsing.memberlevel;

import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.Collection;

import junit.framework.Assert;
import mirrormonkey.util.annotations.control.IRClass;
import mirrormonkey.util.annotations.parsing.AnnotationParser;
import mirrormonkey.util.annotations.parsing.BeanFieldIR;
import mirrormonkey.util.annotations.parsing.ClassIR;
import mirrormonkey.util.annotations.parsing.MemberIR;

import org.junit.BeforeClass;
import org.junit.Test;

/**
 * Contains test cases for <tt>BeanFieldIR</tt>, which collects fields, getters
 * and setters for that field as well as every annotation declared on one of
 * them in the class hierarchy.<br>
 * 
 * Please note that as <tt>BeanFieldIR</tt> is considered an extension class,
 * this test suite does only contain tests for that extended functionality. It
 * is assumed that the basic test cases check out fine.
 * 
 * @author Philipp Christian Loewner
 * 
 */
public class BeanFieldIRTest {

	public static AnnotationParser parser;

	@BeforeClass
	public static void setUpBeforeClass() {
		parser = new AnnotationParser();
	}

	public static void checkContains(ClassIR ir, Field field, Method getter,
			Method setter, Member searchFor) {
		BeanFieldIR bir = null;
		if (searchFor == null) {
			for (Collection<MemberIR> irs : ir.memberIRs.values()) {
				if (!irs.isEmpty()) {
					bir = (BeanFieldIR) irs.iterator().next();
				}
			}
		} else {
			for (MemberIR i : ir.memberIRs.get(searchFor.getClass())) {
				if (i.matches(searchFor)) {
					bir = (BeanFieldIR) i;
				}
			}
		}
		Assert.assertEquals(field, bir.getField().getCollectedField());
		Assert.assertEquals(getter, bir.getGetter().getCollectedMethod());
		Assert.assertEquals(setter, bir.getSetter().getCollectedMethod());
	}

	public static class FirstValidityCheckContainer {

		@IRClass(BeanFieldIR.class)
		public Object field;

		@IRClass(BeanFieldIR.class)
		public Object getGetterField() {
			return null;
		}

		@IRClass(BeanFieldIR.class)
		public void setSetterField(Object field) {
		}

	}

	/**
	 * Tests if <tt>BeanFieldIRs</tt> are created for fields, getters and
	 * setters alike if they were the first IR class to be encountered for that
	 * field and if the member for which the IR has been created is collected
	 * automatically.
	 */
	@Test
	public void testValidityAsFirst() throws Exception {
		ClassIR ir = parser.parseClass(FirstValidityCheckContainer.class, null);
		Assert.assertEquals(3, ir.memberIRs.size());
		Class<?> c = FirstValidityCheckContainer.class;
		checkContains(ir, c.getField("field"), null, null, c.getField("field"));
		checkContains(ir, null, c.getMethod("getGetterField"), null,
				c.getMethod("getGetterField"));
		checkContains(ir, null, null,
				c.getMethod("setSetterField", Object.class),
				c.getMethod("setSetterField", Object.class));
	}

	public static class OverrideValidityCheckContainer {

		@IRClass
		public Object field;

	}

	public static class ExtendingOverrideValidityCheckContainer {

		@IRClass(BeanFieldIR.class)
		public Object field;

	}

	/**
	 * Tests if <tt>BeanFieldIRs</tt> are created for fields where there was the
	 * default <tt>MemberDataIR</tt> previously present.
	 */
	@Test
	public void testExtendingOverride() throws Exception {
		ClassIR ir = parser.parseClass(
				ExtendingOverrideValidityCheckContainer.class, null);
		checkContains(
				ir,
				ExtendingOverrideValidityCheckContainer.class.getField("field"),
				null, null,
				ExtendingOverrideValidityCheckContainer.class.getField("field"));
	}

	public static void checkContainsAll(ClassIR ir, Class<?> base,
			String fieldName) throws Exception {
		checkContains(ir, base.getField("field"), base.getMethod("getField"),
				base.getMethod("setField", Object.class), null);
	}

	public static class FirstDefineField {

		@IRClass(BeanFieldIR.class)
		public Object field;

		public Object getField() {
			return null;
		}

		public void setField(Object o) {
		}

	}

	/**
	 * Tests if matching getters and setters are collected if the first item
	 * annotated was a field.
	 */
	@Test
	public void testCollectGetterSetter() throws Exception {
		ClassIR ir = parser.parseClass(FirstDefineField.class, null);
		checkContainsAll(ir, FirstDefineField.class, "field");
	}

	public static class FirstDefineGetter {

		public Object field;

		@IRClass(BeanFieldIR.class)
		public Object getField() {
			return null;
		}

		public void setField(Object o) {
		}

	}

	/**
	 * Tests if matching field and setter are collected if the first item
	 * annotated was a getter.
	 */
	@Test
	public void testCollectFieldSetter() throws Exception {
		ClassIR ir = parser.parseClass(FirstDefineGetter.class, null);
		checkContainsAll(ir, FirstDefineGetter.class, "field");
	}

	public static class FirstDefineSetter {

		public Object field;

		public Object getField() {
			return null;
		}

		@IRClass(BeanFieldIR.class)
		public void setField(Object o) {
		}

	}

	/**
	 * Tests if matching field and getter are collected if the first item
	 * annotated was a setter.
	 */
	@Test
	public void testCollectFieldGetter() throws Exception {
		ClassIR ir = parser.parseClass(FirstDefineSetter.class, null);
		checkContainsAll(ir, FirstDefineSetter.class, "field");
	}

}
